<template>
  <div class="dv-scroll-board" :ref="ref">
    <div class="header" v-if="header.length && mergedConfig && mergedConfig.showHeader"
         :style="`background-color: ${mergedConfig.headerBGC}; color: ${mergedConfig.headerTextColor}; font-size: ${mergedConfig.headerFontSize}px;`">
      <div
          class="header-item"
          v-for="(headerItem, i) in header"
          :key="headerItem + i"
          :style="`
            height: ${mergedConfig.headerHeight}px;
            line-height: ${mergedConfig.headerHeight}px;
            width: ${widths[i]}px;
          `"
          :align="aligns[i]"
          v-html="headerItem"
      />
    </div>

    <div
        v-if="mergedConfig"
        class="rows"
        :style="`height: ${height - (mergedConfig.showHeader && header.length ? mergedConfig.headerHeight : 0)}px;`"
    >
      <div
          class="row-item"
          v-for="(row, ri) in rows"
          :key="row.scroll"
          :style="`
            height: ${heights[ri]}px;
            background-color: ${mergedConfig[row.rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']};
          `"
      >
        <div
            class="ceil"
            v-for="(ceil, ci) in row.ceils"
            :key="ri + ci"
            :style="`width: ${widths[ci]}px; display: flex; align-items: center;`"
            @click="emitEvent(ri, ci, row, ceil)"
        >
          <p v-html="ceil"
             :style="`flex: 1; text-align: ${aligns[ci]}; font-size: ${fontSizes[ci]}; color: ${textColors[ci]};`"></p>
        </div>

      </div>
    </div>
  </div>
</template>

<script>

import autoResize from '@/utils/PnScrollBoardAutoResize'

import {deepMerge} from '@jiaminghi/charts/lib/util/index'

import {deepClone} from '@jiaminghi/c-render/lib/plugin/util'

export default {
  name: 'PnScrollBoard',
  mixins: [autoResize],
  props: {
    config: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      ref: 'scroll-board',

      defaultConfig: {
        header: [],
        data: [],
        rowNum: 5,
        showHeader: true,
        headerBGC: '#00BAFF',
        headerTextColor: '#fff',

        oddRowBGC: '#003B51',
        evenRowBGC: '#0A2732',
        waitTime: 2000,
        headerHeight: 35,
        columnWidth: [],
        align: [],
        index: false,
        indexHeader: '#',
        carousel: 'single'
      },

      mergedConfig: null,
      header: [],
      rowsData: [],
      rows: [],
      widths: [],
      heights: [],
      avgHeight: 0,
      aligns: [],
      fontSizes: [],
      textColors: [],
      clipNums: [],
      animationIndex: 0,
      animationHandler: '',
      updater: 0
    }
  },
  watch: {
    config: {
      handler: function () {
        const {stopAnimation, calcData} = this

        stopAnimation()

        calcData()
      },
      deep: true
    }
  },
  methods: {
    afterAutoResizeMixinInit() {
      const {calcData} = this

      calcData()
    },
    onResize() {
      const {mergedConfig, calcWidths, calcHeights} = this

      if (!mergedConfig) return

      calcWidths()

      calcHeights()
    },
    calcData() {
      const {mergeConfig, calcHeaderData, calcRowsData} = this

      mergeConfig()
      calcHeaderData()
      calcRowsData()
      const {calcWidths, calcHeights, calcAligns, calcFontSizes, calcTextColors} = this
      calcWidths()
      calcHeights()
      calcAligns()
      calcFontSizes()
      calcTextColors()

      const {animation} = this
      animation(true)
    },
    mergeConfig() {
      let {config, defaultConfig} = this

      this.mergedConfig = deepMerge(deepClone(defaultConfig, true), config || {})
    },
    calcHeaderData() {
      let {header, index, indexHeader} = this.mergedConfig

      if (!header.length) {
        this.header = []

        return
      }

      header = [...header]

      if (index) header.unshift(indexHeader)

      this.header = header
    },
    calcRowsData() {
      let {data, index, headerBGC, rowNum} = this.mergedConfig

      if (index) {
        data = data.map((row, i) => {
          row = [...row]

          const indexTag = `<span class="index" style="background-color: ${headerBGC};">${i + 1}</span>`

          row.unshift(indexTag)

          return row
        })
      }

      data = data.map((ceils, i) => ({ceils, rowIndex: i}))

      const rowLength = data.length

      if (rowLength > rowNum && rowLength < 2 * rowNum) {
        data = [...data, ...data]
      }

      data = data.map((d, i) => ({...d, scroll: i}))

      this.rowsData = data
      this.rows = data
    },
    calcWidths() {
      const {width, mergedConfig, rowsData} = this

      const {columnWidth, header} = mergedConfig

      const usedWidth = columnWidth.reduce((all, w) => all + w, 0)

      let columnNum = 0
      if (rowsData[0]) {
        columnNum = rowsData[0].ceils.length
      } else if (header.length) {
        columnNum = header.length
      }

      const avgWidth = (width - usedWidth) / (columnNum - columnWidth.length)

      const widths = new Array(columnNum).fill(avgWidth)

      this.widths = deepMerge(widths, columnWidth)
    },
    calcHeights(onresize = false) {
      const {height, mergedConfig, header} = this

      const {headerHeight, rowNum, data} = mergedConfig

      let allHeight = height

      if (header.length) allHeight -= headerHeight

      const avgHeight = allHeight / rowNum

      this.avgHeight = avgHeight

      if (!onresize) this.heights = new Array(data.length).fill(avgHeight)
    },
    calcAligns() {
      const {header, mergedConfig} = this

      const columnNum = header.length

      let aligns = new Array(columnNum).fill('left')

      const {align} = mergedConfig

      this.aligns = deepMerge(aligns, align)
    },
    calcFontSizes() {
      const {mergedConfig} = this
      this.fontSizes = [];

      if (mergedConfig.index) {
        this.fontSizes.push('inherit')
      }
      mergedConfig.columnMapping.forEach(columnMappingItem => {
        this.fontSizes.push(columnMappingItem.fontSize + 'px')
      })
    },
    calcTextColors() {
      const {mergedConfig} = this
      this.textColors = [];

      if (mergedConfig.index) {
        this.textColors.push('inherit')
      }
      mergedConfig.columnMapping.forEach(columnMappingItem => {
        this.textColors.push(columnMappingItem.textColor)
      })
    },
    async animation(start = false) {
      let {avgHeight, animationIndex, mergedConfig, rowsData, animation, updater} = this

      const {waitTime, carousel, rowNum} = mergedConfig

      const rowLength = rowsData.length

      if (rowNum >= rowLength) return

      if (start) {
        await new Promise(resolve => setTimeout(resolve, waitTime))
        if (updater !== this.updater) return
      }

      const animationNum = carousel === 'single' ? 1 : rowNum

      let rows = rowsData.slice(animationIndex)
      rows.push(...rowsData.slice(0, animationIndex))

      this.rows = rows
      this.heights = new Array(rowLength).fill(avgHeight)

      await new Promise(resolve => setTimeout(resolve, 300))
      if (updater !== this.updater) return

      this.heights.splice(0, animationNum, ...new Array(animationNum).fill(0))

      animationIndex += animationNum

      const back = animationIndex - rowLength
      if (back >= 0) animationIndex = back

      this.animationIndex = animationIndex
      this.animationHandler = setTimeout(animation, waitTime - 300)
    },
    stopAnimation() {
      const {animationHandler, updater} = this

      this.updater = (updater + 1) % 999999

      if (!animationHandler) return

      clearTimeout(animationHandler)
    },
    emitEvent(ri, ci, row, ceil) {
      const {ceils, rowIndex} = row

      this.$emit('click', {
        row: ceils,
        ceil,
        rowIndex,
        columnIndex: ci
      })
    }
  },
  destroyed() {
    const {stopAnimation} = this

    stopAnimation()
  }
}
</script>

<style lang="less">
.dv-scroll-board {
  position: relative;
  width: 100%;
  height: 100%;
  color: #fff;

  .text {
    padding: 0 10px;
    box-sizing: border-box;
    //white-space: nowrap;
    overflow: hidden;
    //text-overflow: ellipsis;
  }

  .header {
    display: flex;
    flex-direction: row;
    font-size: 15px;

    .header-item {
      .text;
      transition: all 0.3s;
    }
  }

  .rows {
    overflow: hidden;

    .row-item {
      display: flex;
      font-size: 14px;
      transition: all 0.3s;
    }

    .ceil {
      .text;
    }

    .index {
      border-radius: 3px;
      padding: 0px 3px;
    }
  }
}
</style>
